package com.suhuamo.transtionals.service;

import com.suhuamo.transtionals.mapper.UserMapper;
import com.suhuamo.transtionals.pojo.User;
import com.sun.org.apache.xerces.internal.dom.AbortException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.io.IOException;

/**
 * @author yuanchuncheng
 * @slogan yscz
 * @date 2024-10-18
 * @description
 */
@Service
public class UserService {
    @Autowired
    UserMapper userMapper;
    
    public static final int id = 1;
    
    public int count() {
        User user = userMapper.selectById(id);
        return user.getCount();
    }

    /**
     * 未做事务处理的异常演示——数据库成功写入
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    public void demo_1_1() {
        User user = userMapper.selectById(id);
        user.addCount();
        userMapper.updateById(user);
        throw new RuntimeException();
        /// ... 后续逻辑
    }

    /**
     * 带有事务的异常演示——数据库写入失败
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    @Transactional
    public void demo_1_2() {
        User user = userMapper.selectById(id);
        user.addCount();
        userMapper.updateById(user);
        throw new RuntimeException();
        /// ... 后续逻辑
    }

    /**
     * RuntimeException、及RuntimeException以下的异常默认才会被事务捕获
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    @Transactional
    public void demo_2_1_1() {
        User user = userMapper.selectById(id);
        user.addCount();
        userMapper.updateById(user);
        throw new RuntimeException();
        /// ... 后续逻辑
    }

    /**
     * RuntimeException、及RuntimeException以下的异常默认才会被事务捕获
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    @Transactional
    public void demo_2_1_2() {
        User user = userMapper.selectById(id);
        user.addCount();
        userMapper.updateById(user);
        throw new AbortException();
        /// ... 后续逻辑
    }

    // 自定义注解，只要是继承了 RuntimeException 事务默认都会捕获
    public class YSRunTimeException extends RuntimeException {

    }

    /**
     * RuntimeException、及RuntimeException以下的异常默认才会被事务捕获
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    @Transactional
    public void demo_2_1_3() {
        User user = userMapper.selectById(id);
        user.addCount();
        userMapper.updateById(user);
        throw new YSRunTimeException();
        /// ... 后续逻辑
    }

    /**
     * RuntimeException以外的事务默认不会被捕获【Exception是RuntimeException的上层，也不会被捕获】
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    @Transactional
    public void demo_2_2_1() throws Exception {
        User user = userMapper.selectById(id);
        user.addCount();
        userMapper.updateById(user);
        throw new Exception("123");
        /// ... 后续逻辑
    }

    /**
     * RuntimeException以外的事务默认不会被捕获【Exception是RuntimeException的上层，也不会被捕获】
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    @Transactional
    public void demo_2_2_2() throws IOException {
        User user = userMapper.selectById(id);
        user.addCount();
        userMapper.updateById(user);
        throw new IOException();
        /// ... 后续逻辑
    }

    // 自定义注解，不是继承了 RuntimeException 事务默认不会捕获
    public class YSNORunTimeException extends Exception {

    }

    /**
     * RuntimeException以外的事务默认不会被捕获【Exception是RuntimeException的上层，也不会被捕获】
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    @Transactional
    public void demo_2_2_3() throws YSNORunTimeException {
        User user = userMapper.selectById(id);
        user.addCount();
        userMapper.updateById(user);
        throw new YSNORunTimeException();
        /// ... 后续逻辑
    }

    @Autowired
    BService bService;

    /**
     * 不同类使用事务--本类无事务
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    public void demo_3_1_1() {
        bService.haveTransactional();
        bService.noTransactional();
    }

    /**
     * 不同类使用事务--本类无事务
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    public void demo_3_1_2() {
        bService.noTransactional();
        bService.haveTransactional();
    }

    /**
     * 不同类使用事务--本类有事务
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    @Transactional
    public void demo_3_1_3() {
        bService.haveTransactional();
        bService.noTransactional();
    }

    /**
     * 不同类使用事务--本类有事务
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    @Transactional
    public void demo_3_1_4() {
        bService.noTransactional();
        bService.haveTransactional();
    }

    /**
     * 同一个类使用事务--本方法无事务
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    public void demo_3_2_1() {
        demo_1_1();
        demo_1_2();
    }

    /**
     * 同一个类使用事务--本方法无事务
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    public void demo_3_2_2() {
        demo_1_2();
        demo_1_1();
    }

    /**
     * 同一个类使用事务--本方法有事务
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    @Transactional
    public void demo_3_2_3() {
        demo_1_1();
        demo_1_2();
    }

    /**
     * 同一个类使用事务--本方法有事务
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    @Transactional
    public void demo_3_2_4() {
        demo_1_2();
        demo_1_1();
    }

    /**
     * 手动处理事务——手动回滚:代表我不想抛出异常，但是也想达到回滚的效果,
     * 事务控制的是整个方法，该方法会标记事务需要回滚，故不管在方法的哪个地方调用
     * setRollbackOnly(), 方法内的所有内容均会回滚
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    @Transactional
    public int demo_4_1() {
        User user = userMapper.selectById(id);
        user.addCount();
        userMapper.updateById(user);
        //手工回滚异常
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        System.out.println("继续执行demo_4_1");
        user.addCount();
        userMapper.updateById(user);
        return 1;
    }

    /**
     * 手动处理事务——指定部分回滚:代表我不想抛出异常，但是也想达到回滚的效果
     * savePoint 到执行 rollbackToSavepoint 部分的代码被标记为事务需要回滚，即执行完强制回滚
     * 其他部分的代码正常执行，当然也会被最外面的 Transactional 控制事务回滚【若方法中抛出异常，使得事务被标记为回滚】
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    @Transactional
    public int demo_4_2() {
        User user = userMapper.selectById(id);
        user.addCount();
        userMapper.updateById(user);
        // ...逻辑代码
        //只回滚以下异常，设置回滚点
        Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
        try {
            user.addCount();
            userMapper.updateById(user);
            throw new Exception("123");
        } catch (Exception e) {
            e.printStackTrace();
            //手工回滚异常，回滚到savePoint
            TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);
        }
        // ...逻辑代码
        System.out.println("继续执行demo_4_2");
        user.addCount();
        userMapper.updateById(user);
        return 1;
    }


    @Autowired
    DataSourceTransactionManager dataSourceTransactionManager;
    @Autowired
    TransactionDefinition transactionDefinition;

    /**
     * 手动处理事务——可以解决类内方法事务无法生效的问题
     * @param
     * @return void
     * @version 1.0
     * @author yuanchuncheng
     */
    public void demo_4_3() {
        // 手动创建事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        demo_1_2();
        // 手动回滚事务。(最好是放在catch 里面，防止程序异常而事务一直卡在哪里未提交)
        dataSourceTransactionManager.rollback(transactionStatus);
    }
}
